package com.yilin.tms.order.service.payment.impl;

import com.google.gson.reflect.TypeToken;
import com.yilin.commons.exception.BusinessException;
import com.yilin.commons.util.*;
import com.yilin.tms.core.commons.utils.ParamUtil;
import com.yilin.tms.core.entity.gps.gaode.GeoCalResult;
import com.yilin.tms.core.entity.order.goods.GoodsOrder;
import com.yilin.tms.core.entity.system.param.CommTruckType;
import com.yilin.tms.core.commons.model.CalculateCost;
import com.yilin.tms.core.entity.user.UserType;
import com.yilin.tms.core.entity.user.hatch.HatchPricingRule;
import com.yilin.tms.core.entity.user.hatch.HatchPricingRuleItem;
import com.yilin.tms.core.entity.user.organize.MemberType;
import com.yilin.tms.core.entity.user.organize.Organize;
import com.yilin.tms.core.entity.user.technical.TechnicalFeeRule;
import com.yilin.tms.core.entity.user.technical.TechnicalFeeRuleItem;
import com.yilin.tms.order.service.payment.ICalculationService;
import com.yilin.tms.core.remote.IGpsRemote;
import com.yilin.tms.core.remote.ISystemRemote;
import com.yilin.tms.core.remote.IUserRemote;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Service
public class CalculationService implements ICalculationService {

    @Resource
    IUserRemote userRemote;
    @Resource
    ISystemRemote systemRemote;
    @Resource
    IGpsRemote gpsRemote;

    /**
     * 计算货主支付费用合计
     * 可以只计算手续费，税费
     */
    @Override
    public CalculateCost getShipperCalculateCost(String shipperOrganizeId, GoodsOrder.PriceMode priceMode, Double freight, String startLngLats,
                                                      String endLngLats, String truckTypeIds, Boolean matchmaking, Boolean includedTax) {
        if (priceMode == null) throw new BusinessException("请指定计价模式");
        CalculateCost calculateCost = new CalculateCost();
        Organize organize = ParamUtil.isSuccess(userRemote.getOrganizeById(shipperOrganizeId));
        if (organize.getUserType()!=UserType.shipperUser) throw new BusinessException("此接口只能计算货主运输费！");
        if (freight == null || priceMode == GoodsOrder.PriceMode.notPrice) freight = 0D;
        // 根据多点位置计算距离和时间
        GeoCalResult geoCalResult = ParamUtil.isSuccess(gpsRemote.getDrivingCalculation(organize.getSuperiorPlatOrganizeId(), startLngLats, endLngLats));
        calculateCost.setDistance(geoCalResult.getDistance());
        calculateCost.setDuration(geoCalResult.getDuration());
        // 如果费用为空，并且是整车计价，由平台自动生成费用
        if (priceMode == GoodsOrder.PriceMode.wholePrice && StringUtil.isNotEmpty(truckTypeIds)) {
            freight = calculateTechnicalFee(organize.getSuperiorPlatOrganizeId(), truckTypeIds, geoCalResult.getDistance(), matchmaking);
        }
        calculateCost.setFreight(CommonUtil.doubleTwo(freight));
        // 获取平台下会员费率
        MemberType memberType = ParamUtil.isSuccess(userRemote.getMyMemberTypeByOrganizeId(shipperOrganizeId));
        // 平台收取货主额外税费
        double ownerTaxCost = 0.00D;
        if (!includedTax) ownerTaxCost = MathUtil.multiply(memberType.getOwnerTaxRate(), freight,2);
        calculateCost.setExtraTaxCost(CommonUtil.doubleTwo(ownerTaxCost));
        // 收取服務费，撮合/承运
        double carrierFeeCost = MathUtil.multiply(memberType.getShipperCarrierFeeRate(), freight,2);
        double matchFeeCost = MathUtil.multiply(memberType.getShipperMatchFeeRate(), freight,2);
        if (matchmaking) calculateCost.setFeeCost(CommonUtil.doubleTwo(matchFeeCost));
        else calculateCost.setFeeCost(CommonUtil.doubleTwo(carrierFeeCost));
        // 需支付费用合计
        calculateCost.setTotalCost(CommonUtil.doubleTwo(MathUtil.add(calculateCost.getFreight(), calculateCost.getExtraTaxCost(), calculateCost.getFeeCost())));
        // 发布时需支付费用
        calculateCost.setReleasePayCost(CommonUtil.doubleTwo(MathUtil.multiply(calculateCost.getTotalCost(), memberType.getReleasePaymentRate(),2)));
        return calculateCost;
    }

    /**
     * 计算车主支付费用合计
     */
    @Override
    public CalculateCost getTruckerCalculateCost(String truckerOrganizeId, Double freight, Boolean matchmaking) {
        CalculateCost calculateCost = new CalculateCost();
        Organize organize = ParamUtil.isSuccess(userRemote.getOrganizeById(truckerOrganizeId));
        if (organize.getUserType()!=UserType.truckerUser) throw new BusinessException("此接口只能计算车主运输费！");
        MemberType memberType = ParamUtil.isSuccess(userRemote.getMyMemberTypeByOrganizeId(truckerOrganizeId));
        if (matchmaking) {
            calculateCost.setTruckerFeeCost(MathUtil.multiply(memberType.getTruckerMatchFeeRate(), freight,2));
        } else {
            calculateCost.setTruckerFeeCost(MathUtil.multiply(memberType.getTruckerCarrierFeeRate(), freight,2));
        }
        calculateCost.setAddedTaxCost(CommonUtil.doubleTwo(MathUtil.multiply(memberType.getAddedTaxRate(), freight,2)));
        calculateCost.setCityTaxCost(CommonUtil.doubleTwo(MathUtil.multiply(memberType.getCityTaxRate(), freight,2)));
        calculateCost.setLocalEduTaxCost(CommonUtil.doubleTwo(MathUtil.multiply(memberType.getLocalEduTaxRate(), freight,2)));
        calculateCost.setEduAddTaxCost(CommonUtil.doubleTwo(MathUtil.multiply(memberType.getEduAddTaxRate(), freight,2)));
        calculateCost.setIncomeTaxCost(CommonUtil.doubleTwo(MathUtil.multiply(memberType.getIncomeTaxRate(), freight,2)));
        double truckerTotalTaxCost = MathUtil.add(calculateCost.getAddedTaxCost(), calculateCost.getCityTaxCost(),
                calculateCost.getLocalEduTaxCost(), calculateCost.getEduAddTaxCost(), calculateCost.getIncomeTaxCost());
        calculateCost.setTruckerTotalTaxCost(CommonUtil.doubleTwo(truckerTotalTaxCost));
        return calculateCost;
    }

    //--------------------------------------------------------------------------------------------------------------------------------------

    /**
     * 车型计价
     * 通过计价规则项完成计价运算
     */
    @Override
    public double calculateTechnicalFee(String platId, String truckTypeIds, double distance, boolean match) {
        double cost;
        //检查 车型ID 是否在同一类别下
        String commTruckTypeId = getCommTruckTypeIdByChildId(truckTypeIds);
        //获取 平台计价规则
        Map<String,Map<String, Object>> ruleMap =ParamUtil.isSuccess(userRemote.getCommTruckTypeRule(platId, commTruckTypeId));
        //检查 孵化配置是否就位
        if (ruleMap.get("match") == null) throw new BusinessException("平台未初始化规则配置！");
        if (ruleMap.get("carrier") == null) throw new BusinessException("平台未初始化规则配置！");
        // 完成：根据 承运/撮合 获取费用
        if (match) cost = ruleCalculate(JsonUtil.mapToObject(ruleMap.get("match"),new TypeToken<HatchPricingRule>(){}), distance/1000);
        else cost = ruleCalculate(JsonUtil.mapToObject(ruleMap.get("carrier"),new TypeToken<HatchPricingRule>(){}), distance/1000);
        return cost;
    }

    /**
     * 车型计价
     * 阶梯费用计算
     */
    @Override
    public double ruleCalculate(HatchPricingRule pricingRule, double kilometer) {
        final double finalKilometer = Math.ceil(kilometer);
        List<HatchPricingRuleItem> ruleItems = ArrayUtil.whereBy(pricingRule.getRuleItems(), obj -> !(obj.getStartDistance() > finalKilometer));
        //### 遍历分段，计算费用
        double totalCost = 0.0D;
        for (HatchPricingRuleItem item : ruleItems) {
            double splitDis, splitCost;
            // ### 计算分段公里数，如果实际公里数超出，直接等于设计的分段大小值，区间规则：不含头含尾
            if (item.getEndDistance() == null || finalKilometer < item.getEndDistance()) {
                splitDis = MathUtil.subtract(finalKilometer - item.getStartDistance());
            } else {
                splitDis = MathUtil.subtract(item.getEndDistance(), item.getStartDistance());
            }
            // ### 根据区间计价规则进行计价，如果是 区间计价 直接相等,否则相乘
            if (!item.getIsUnit()) splitCost = item.getPrice();
            else splitCost = MathUtil.multiply(splitDis, item.getPriceUnit(),2);
            // ### 完成，可以把分段信息返回到前端，这里暂时只返回最终结果值
            totalCost = MathUtil.add(totalCost, splitCost);
        }
        return totalCost;//100.053
    }

    /**
     * 车型计价
     * 获取类别车型ID
     * 获取上级车型，如果存在多类车型，还会对车型类别进行检查
     */
    @Override
    public String getCommTruckTypeIdByChildId(String truckTypeIds) {
        List<CommTruckType> truckTypeList = ParamUtil.isSuccess(systemRemote.getCommTruckTypeListByIds(truckTypeIds.split(",")));
        if (truckTypeList == null || truckTypeList.size() == 0) return null;
        String commTruckTypeId = null;//这个是类别车型
        for (CommTruckType commTruckType : truckTypeList) {
            if (commTruckTypeId == null) commTruckTypeId = commTruckType.getParentId();
            else if (!commTruckTypeId.equals(commTruckType.getParentId())) {
                throw new BusinessException("车型检查失败，含有多类别车型！");
            }
        }
        return commTruckTypeId;
    }

    //--------------------------------------------------------------------------------------------------------------------------------------


    //计算运营服务费
    //运营服务按比例收取
    @Override
    public double calculateOperateFee(String platId, Double transportCost) {
        MemberType memberType = ParamUtil.isSuccess(userRemote.getMyMemberTypeByOrganizeId(platId));
        Double feeRate = memberType.getBelowPlatFeeRate();
        return MathUtil.multiply(transportCost, feeRate,2);
    }

    //计算技术服务费 - 下级平台
    //技术服务费按比例收取
    @Override
    public double calculatePlatTechnicalFee(String platId, Double transportCost) {
        MemberType memberType = ParamUtil.isSuccess(userRemote.getMyMemberTypeByOrganizeId(platId));
        Double feeRate = memberType.getBelowTechyFeeRate();
        return MathUtil.multiply(transportCost, feeRate,2);
    }

    //计算技术服务费-司机
    //通过计价规则项完成计价运算
    @Override
    public double calculateTechnicalFee(Double cost) {
        TechnicalFeeRule feeRule = ParamUtil.isSuccess(userRemote.getTechnicalFeeRule());
        if (feeRule==null) return 0.0d;
        return ruleCalculate(feeRule, cost);
    }

    //阶梯费用计算
    //阶梯和车型不一样，这里不是分段计费，而是条件计费，即使用满足最大条件的一项即可
    private double ruleCalculate(TechnicalFeeRule feeRule, Double cost) {
        double totalFeeCost;
        final double finalCost = Math.ceil(cost);
        List<TechnicalFeeRuleItem> ruleItems = ArrayUtil.whereBy(feeRule.getRuleItems(), obj -> !(obj.getStartFee() > finalCost));
        ruleItems.sort((o1, o2) -> o2.getStartFee().compareTo(o1.getStartFee()));
        TechnicalFeeRuleItem first = ArrayUtil.first(ruleItems);
        if (!first.getIsRate()) totalFeeCost = first.getFixCost();
        else totalFeeCost = MathUtil.multiply(finalCost, first.getRateCost() / 100,2);//注意百分比
        return totalFeeCost;
    }


}
